package com.java.util;

import com.java.util.pojo.RequestContent;
import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Ad创建校验
 */
public class AdSearchV1 {


    private static AtomicInteger count = new AtomicInteger(0);


    public static void main(String[] args) {
        List<List<RequestContent>> allList = new ArrayList<>();


        for (int i = 0; i < 2000000; i++) {
            List<RequestContent> tempList = new ArrayList<>();
            tempList.add(new RequestContent(1, "aa-1", "h1111", 1, false));
            tempList.add(new RequestContent(1, "aa-1", "h1111", 1, false));
            tempList.add(new RequestContent(1, "aa-1", "h1111", 1, false));
            tempList.add(new RequestContent(1, "aa-1", "h1111", 1, false));
            tempList.add(new RequestContent(3, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(2, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(5, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(4, "bb-2", "h1111", 1, false));
            tempList.add(new RequestContent(2, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(5, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(4, "bb-2", "h1111", 1, false));
            tempList.add(new RequestContent(2, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(5, "aa-2", "h1111", 1, false));
            tempList.add(new RequestContent(4, "bb-2", "h1112", 2, false));
            tempList.add(new RequestContent(2, "aa-2", "h1113", 0, false));
            allList.add(tempList);
        }


        Set<String> validFailedSet = Collections.synchronizedSet(new HashSet<>());

        long startTime = System.currentTimeMillis();

        allList.parallelStream().forEach(list -> {
            List<RequestContent> combinations = new ArrayList<>();

            findValidDetailByBacktrack(list, 0, new ArrayList<>(), 3, combinations);

            if (CollectionUtils.isEmpty(combinations)) {
                validFailedSet.add("无效数据 老方法~");
            }
        });
        long endTime = System.currentTimeMillis();
        System.out.println(">>>>>>>>>>> cost time " + (endTime - startTime) / 1000);



        long startTime1 = System.currentTimeMillis();
        allList.parallelStream().forEach(list -> {
            Boolean valid = valid(list, 3,6);
            if (!valid) {
                validFailedSet.add("无效数据 新方法~");
            }
        });
        long endTime1 = System.currentTimeMillis();
        System.out.println(">>>>>>>>>>>>>>> cost " + (endTime1 - startTime1) / 1000);

        System.out.println("最终结果： " + validFailedSet);

    }

    private static void findValidDetailByBacktrack(List<RequestContent> adDetailList, int start, List<RequestContent> currentList, final int requiredCount, List<RequestContent> result) {
        if (currentList.size() == requiredCount) {
            if (idGood(currentList, requiredCount)) {
                result.addAll(currentList);
                return;
            }
            return;
        }

        for (int i = start; i < adDetailList.size(); i++) {
            currentList.add(adDetailList.get(i));
            // 注意这里从i+1开始，避免重复选择同一个元素，因为只考虑组合，而不是排列组合
            findValidDetailByBacktrack(adDetailList, i + 1, currentList, requiredCount, result);

            // 判读是否需要回溯，符合条件终止方法，不符合条件就回溯。
            if (result.size() ==  requiredCount) {
                return;
            } else {
                currentList.remove(currentList.size() - 1);
            }
        }
    }

    private static Boolean valid(List<RequestContent> list, int requiredCount,int rqSum ) {

        Map<String, List<Integer>> map = new HashMap<>();
        int i = 0;
        while (i < list.size()) {
            Boolean valid = beValid(map, requiredCount,rqSum);
            if (!valid) {
                RequestContent o = list.get(i);
                List<Integer> pinSet = map.getOrDefault(o.getPreviewContent(), new ArrayList<>());
                pinSet.add(o.getPinMode());
                map.put(o.getPreviewContent(), pinSet);
                i++;
            } else {
                return true;
            }
        }
        return beValid(map, requiredCount,rqSum);
    }

    private static Boolean beValid(Map<String, List<Integer>> map, int requiredCount,int rqSum ) {
        if (map.size() < requiredCount) {
            return Boolean.FALSE;
        }
        AtomicInteger zeroCount = new AtomicInteger();
        Map<Integer, List<Integer>> pinCountMap = new HashMap<>();
        map.values().stream()
                .flatMap(Collection::stream)
                .forEach(i -> {
                    if (i == 0) {
                        zeroCount.getAndIncrement();
                        return;
                    }
                    List<Integer> list = pinCountMap.getOrDefault(i, new ArrayList<>());
                    list.add(i);
                    pinCountMap.put(i, list);
                });

        if (zeroCount.get() > requiredCount) {
            return Boolean.TRUE;
        }
        Set<Integer> maxPinSet = new HashSet<>();
        pinCountMap.values()
                .stream()
                .flatMap(Collection::stream)
                .filter(i -> i != 0)
                .max(Integer::compareTo)
                .ifPresent(maxPinSet::add);

        int sum = maxPinSet.stream().mapToInt(e -> e).sum();

        if (sum >= rqSum) {
            return Boolean.TRUE;
        }

        int diff = requiredCount - pinCountMap.keySet().size();
        if (zeroCount.get() >= diff) {
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }


    private static Boolean idGood(List<RequestContent> adDetailList, final int requiredCount) {
        if (CollectionUtils.isEmpty(adDetailList) || adDetailList.size() < requiredCount) {
            return Boolean.FALSE;
        }
        // 进来的是组合的3 个详情，判断是不是符合条件，去重content，判断pin是否够用
        Map<String, List<RequestContent>> tempMap = adDetailList.stream().collect(Collectors.groupingBy(RequestContent::getPreviewContent));

        // 去重之后数量小于给定的值，也不用比较pin了
        if (tempMap.size() < requiredCount) {
            return Boolean.FALSE;
        }

        // 校验pin position
        // 如果Any的个数加上其他pin1/pin2/pin3的个数等于要求的个数就认为是对的。
        Map<Integer, Long> collect = adDetailList.stream().collect(Collectors.groupingBy(RequestContent::getPinMode, Collectors.counting()));

        Long anyCount = collect.getOrDefault(0, 0L);
        long pinCount = collect.keySet().stream().filter(i -> 0 != i).count();

        if ((anyCount + pinCount) == requiredCount) {
            return Boolean.TRUE;
        }

        return Boolean.FALSE;
    }
}
